home *** CD-ROM | disk | FTP | other *** search
- From: cs94169@assn013.cs.ualberta.ca (David Bond)
- Newsgroups: rec.games.programmer
- Subject: VESA SVGA - line code and info
- Date: 6 Feb 1995 18:39:08 GMT
-
- Hello everyone!
-
- This is a mini-tutorial, and code, relating to VESA SVGA programming. The
- code includes a line procedure which is based upon Bresenham's algorithm. It
- is not blazingly fast, but hopefully it'll work on all SVGA cards with VESA
- support, and it is pretty compact - no special cases for slopes.
-
- Many people try to begin programming in SVGA modes straight from mode 13h, or
- Xmode variants. They quickly encounter the problem of only 64K of vid mem
- being accessable - falls a little short of the 300K required for 640x480x8bit!
- The special address space A000h - AFFFh must be mapped to different parts of
- the video memory to make use of it. This can be done via VESA functions. If
- you don't yet have 'vesasp12.txt' (PCGPE contains this document) then I
- suggest you get it from x2ftp.oulu.fi /pub/msdos/programming/specs/vesasp12.
- This document details the VESA BIOS extensions used to get info on video
- modes, set video modes, pan across a larger virtual screen, and set the CPU
- window (A000-AFFF) to map to different places in video mem.
-
- Even though VESA provides a common interface for SVGA cards, there are still
- some specifics that have to be dealt with. The 'granularity' of the window
- is the smallest amount by which it can be moved. A 64KB granularity with
- 1MB video memory means the CPU window can be mapped to one of 16 'chunks' in
- this memory. A 4KB granularity has more potential mappings - the window is
- still 64KB in size, but it can be positioned on any 4K boundary in video. I
- know granularitys of 4K, 16K, 32K, and 64K exist. Some cards are switchable
- (actually the only chipset I'm familiar with that has this option is Cirrus
- Logic - defaults to 4K, can be set to 16K. I think this is necessary for
- accessing >1MB).
-
- I see two ways to manage this discrepancy. Code can assume 64K granularity
- always, and the 'bank-switching' routines make sure the window is moved by this
- ammount (4K gran would require inc/dec by 16). The other way is to deal with
- each granularity differently - this is how the line code provided below
- operates.
-
- Finer granularity can speed up rendering. Line drawing will be used to
- illustrate. The linear start address is calculated. The low 16 bits of this
- address are mappable to the 64K window. The high order bits can be used to
- locate the position of the window. With a 64K granularity, the high word is
- our window location, and the low word is the displacement into the window.
- With 4K gran, The low 12 bits are the offset, and remaining high bits are the
- window location. If a line begins near the end of a 64K aligned chunk (linear
- position 123840, say), and continues down a short distance, It'll cross a 64K
- boundary. With 64K gran, the window will have to be moved. Using a 4K
- granularity, the initial offset into a window can be kept below 4096. So,
- lines that aren't too long can always be kept within the starting window.
-
- Another advantage that fine granularity provides is easier alignment with the
- edge of the screen. With a horizontal resolution of 640, 32 lines takes up
- 20KB, which is divisible by 4KB. If all windowing is then limited to be
- aligned on these 20KB bounds, one will never have to worry about overflowing
- past the end of the window while drawing across a scan-line. The windows
- are positioned so that the 'bottom' of the window is on these 20K bounds.
- Inner rendering loops that move across a horizontal line don't bother with
- checking for a 'page-cross'. The outer loop checks for overflow when it moves
- down to the next scan-line. A 64K granularity doesn't align until 512
- vertical lines (320KB), which means the inner loop must check for
- page-crossings within the scan-line.
-
- Note: one way to create easy alignment with the edge is to change the length
- of a scanline to a power of 2 (say 1024). This wastes video memory, but
- it can be well worth it. Check vesasp12.txt for setting this.
-
- The line procedure, below, does take advantage of positioning the top of the
- window as close to the top of the line as it can. Thus window moving for
- mid-length lines is reduced for cards that have smaller granularities. It
- does not take advantage of alignment with the screen edge. The code is made
- to be fairly 'straight-forward', not much fancy is done - it's just simple,
- flexible, small, and I hope easy to understand. One easy optimization to add
- is to check if the endpoints lie in different window addresses - if not, a
- routine without a page-cross check can be called; otherwise the standard
- routine is called.
-
- Careful eyes may notice that lines are always rendered from top to bottom, but
- I have a macro to move the CPU window UP! A situation where this is needed:
- A window begins 382 pixels across on a scan-line. A line is started just two
- pixels into the window (at 383). The endpoint is on the far left of the screen
- (0), and 5 pixels down from start. The line is going to begin with a string of
- pixels straight to the left - passing BACKWARD through the window boundary.
- This occurance requires the 'PageUp' macro. If alignment is done with the
- screen edge, this isn't necessary.
-
-
- This code is provided for learning purposes, and may be used in any fashion
- desired - it's free! If the code doesn't work for you, please let me know. I
- haven't had opportunity to test it on other systems. It didn't get a rigorous
- test on mine either - paging is untested. Conversion to other resolutions is
- pretty simple. The linear address calculation is all that has to be modified
- (I think!?) - 'bx' may be too small at higher resoultions - use 'ebx'.
-
- This can be assembled with: tasm /m2 /ml <filename>
- tlink /3 <filename>
- Or pieces can be extracted, and interfaced to whatever you wish,
- however you wish.
-
- -Anthony Tavener 'Daoloth of MetaSentience'
- -cs94169@cs.ualberta.ca (Temporary - friend's account)
-
- ---CODE BEGIN---
- .486
- code segment para public use16
- assume cs:code
-
- PgDown macro
- push bx
- push dx
- xor bx,bx
- mov dx,cs:winpos
- add dx,cs:disp64k
- mov cs:winpos,dx
- call cs:winfunc
- pop dx
- pop bx
- endm
-
- PgUp macro
- push bx
- push dx
- xor bx,bx
- mov dx,cs:winpos
- sub dx,1
- mov cs:winpos,dx
- call cs:winfunc
- add di,cs:granmask
- inc di
- pop dx
- pop bx
- endm
-
- mov ax,seg stk ;\
- mov ss,ax ;.set up program stack
- mov sp,200h ;/
-
- call GetVESA ;init variables related to VESA support
-
- mov ax,4f02h ;\
- mov bx,0101h ;.VESA mode 101h (640x480x8bit)
- int 10h ;/
-
- mov ax,0a000h
- mov ds,ax
-
- mov eax,10h ;\
- mov ebx,13h
- mov ecx,20bh ;test Lin procedure
- mov edx,1a1h
- mov ebp,21h
- call Lin ;/
-
- mov ax,4c00h
- int 21h
-
- GetVESA proc
- ;This is just a hack to get the window-function address for a direct call,
- ;and to initialize variables based upon the window granularity.
- mov ax,4f01h ;\
- mov cx,0101h
- lea di,buff ;.use VESA mode info call to..
- push cs ;.get card stats for mode 101h
- pop es
- int 10h ;/
- add di,4
- mov ax,word ptr es:[di] ;get window granularity (in KB)
- shl ax,0ah
- dec ax
- mov cs:granmask,ax ; = granularity - 1 (in Bytes)
- not ax
- clc
- GVL1: inc cs:bitshift ;\
- rcl ax,1 ;.just a way to get vars I need :)
- jc GVL1 ;/
- add cs:bitshift,0fh
- inc ax
- mov disp64k,ax
- add di,8
- mov eax,dword ptr es:[di] ;get address of window control
- mov cs:winfunc,eax
- ret
- buff label byte
- db 100h dup (?)
- endp
-
- Lin proc
- ;Codesegment: Lin
- ;Inputs: eax: x1, ebx: y1, cx: x2, dx: y2, bp: color
- ;Destroys: ax, bx, cx, edx, si, edi
- ;Global: winfunc(dd),winpos(dw),page(dw),granmask(dw),disp64k(dw),bitshift(db)
- ;Assumes: eax, ebx have clear high words
-
- cmp dx,bx ;\
- ja LinS1 ;.sort vertices
- xchg ax,cx
- xchg bx,dx ;/
-
- LinS1: sub cx,ax ;\
- ja LinS2 ;.calculate deltax and
- neg cx ;.modify core loop based on sign
- xor cs:xinc1[1],28h ;/
-
- LinS2: sub dx,bx ;deltay
- neg dx
- dec dx
-
- shl bx,7 ;\
- add ax,bx ;.calc linear start address
- lea edi,[eax][ebx*4] ;/
-
- mov si,dx ;\
- xor bx,bx
- mov ax,cs:page ;\
- shl ax,2 ;.pageOffset=page*5*disp64K
- add ax,cs:page
- mul cs:disp64k ;/
- push cx ;.initialize CPU window
- mov cl,cs:bitshift ;.to top of line
- shld edx,edi,cl
- pop cx
- add dx,ax
- and di,cs:granmask
- mov cs:winpos,dx
- call cs:winfunc
- mov dx,si ;/
-
- mov ax,bp
- mov bx,dx
-
- ;ax:color, bx:err-accumulator, cx:deltaX, dx:vertical count,
- ;di:location in CPU window, si:deltaY, bp:color
-
- LinL1: mov [di],al ;\
- add bx,cx
- jns LinS3
- LinE1: add di,280h
- jc LinR2 ;.core routine to
- inc dx ;.render line
- jnz LinL1
- jmp LinOut
- LinL2: mov [di],al ;\
- xinc1 label byte
- LinS3: add di,1 ;.this deals with
- jc LinR1 ;.horizontal pixel runs
- LinE2: add bx,si
- jns LinL2 ;/
- jmp LinE1 ;/
-
- LinR1: js LinS7 ;\
- PgDown ;.move page down 64k..
- mov ax,bp
- jmp LinE2
- LinS7: PgUp ;.or up by 'granularity'
- mov ax,bp
- jmp LinE2 ;/
-
- LinR2: PgDown ;\
- mov ax,bp ;.move page down 64k
- inc dx
- jnz LinL1 ;/
-
- LinOut: mov cs:xinc1[1],0c7h
- ret
- endp
-
- winfunc dd ? ;fullpointer to VESA setwindow function
- winpos dw ? ;temp storage of CPU window position
- granmask dw ? ;masks address within window granularity
- disp64k dw ? ;number of 'granules' in 64k
- page dw 0 ;video page (0,1,2 for 1MB video)
- bitshift db 0 ;used to extract high order address bits..
- ;\ for setting CPU window
- ends
-
- stk segment para stack use16 'STACK'
- dw 100h dup (?)
- ends
- end
- ---CODE END---
-
-
-